import json
import traceback
from django.dispatch import receiver

from django.http import JsonResponse
from django.shortcuts import render
from django.core.paginator import Paginator
from django.db.models import Q

from bbs_server import settings
from bbs_server.support.token_util import TokenUtil
from bbs_server.models import AudioRecorder, Comment, Notice, Follower, Blocker, Liker, PictureRecorder, Post, User, VideoRecorder;

# 发送请求
FIELD_PARAM_1 = 'param1'
FIELD_USER_ID = 'user_id'
FIELD_POST_ID = "post_id"
FIELD_NOTICE_ID = 'notice_id'
FIELD_PARAM_DATA = 'request_data'
# 请求返回
FIELD_REPLY = 'reply'
FIELD_DATA = 'data'
FIELD_ERROR = 'error_message'

ERROR_TOKEN_INVALID = 'invalid_token'
ERROR_REGISTER_NAME_REPEATED = 'repeated_name'
ERROR_REGISTER_EMAIL_REPEATED = 'repeated_email'
ERROR_IDENTITY_THEFT = "identity_theft"
ERROR_OPERATE_TO_YOURSELF = "should_not_operate_to_yourself"

# request: /
def home(request):
    ctx = {}
    return render(request, 'home.html', ctx)


# request: /
def hello(request):
    return JsonResponse({ FIELD_REPLY: True })


# request: PostQueryArg, partly require Token
def query_post(request):
    TokenUtil.check_token(request)
    request_dict = json.loads(request.body)
    myself = request.user
    print(str(request_dict) + ', user: ' + str(request.user))

    query_set = Post.objects.filter(type=0)

    # --------------- 搜索 ---------------
    keywords = request_dict.get('keywords');
    search_type = int(request_dict.get('searchType'));
    if len(keywords) == 0 or search_type <= 0 or search_type >= 8:
        print('skip search')
    else:
        for keyword in keywords:
            query_conds = Q()
            if not search_type & 0x1 == 0: # title
                query_conds = query_conds | Q(title__contains=keyword)
            if not search_type & 0x2 == 0: # content
                query_conds = query_conds | Q(content__contains=keyword)
            if not search_type & 0x4 == 9: # author
                query_conds = query_conds | Q(author_name__contains=keyword)
            query_set = query_set.filter(query_conds)
    
    # --------------- 筛选 ---------------
    style = int(request_dict.get('style'));
    style_list = []
    for i in range(16):
        if not style & i == 0:
            style_list.append(i)
    query_set = query_set.filter(style__in=style_list)

    # --------------- 屏蔽 ---------------
    if isinstance(myself, User):
        block_id_list = myself.user_host.all().values('enemy__name')
        if len(block_id_list) > 0:
            print("blocked id list: " + str(block_id_list))
            query_set = query_set.exclude(author_name__in=block_id_list)

    # --------------- 关注 ---------------
    if bool(request_dict.get('favoriteOnly')):
        if isinstance(myself, User):
            followed_id_list = myself.user_fan.all().values('star__name')
            print("followed id list: " + str(followed_id_list))
            query_set = query_set.filter(author_name__in=followed_id_list)
        else:
            print('try to get favorite without login, return empty list')
            return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: [] })

    # --------------- 排序 ---------------
    order = int(request_dict.get('order'));
    # TODO: 综合排序
    if order == 1 or order == 0:
        query_set = query_set.order_by('-update_time')
    elif order == 2:
        query_set = query_set.order_by('-like_num')

    print(query_set.query)

    paginator = Paginator(query_set, 10)
    page = request_dict.get('page')
    try:
        page_res = paginator.page(page)
    except:
        page_res = []

    output_list = []
    for var in page_res:
        output_var = {}
        output_var['id'] = var.id
        output_var['title'] = var.title
        output_var['type'] = var.type
        output_var['abstractContent'] = var.abstract
        output_var['authorId'] = var.author.id
        output_var['authorName'] = var.author.name  # author name 在查询的时候使用
        output_var['likeNum'] = var.like_num
        output_var['likeOr'] = False
        if isinstance(myself, User):
            try:
                liker = Liker.objects.get(post__id=var.id, reader__id=myself.id)
                if liker.state:
                    output_var['likeOr'] = True
            except:
                pass # 没有点赞过
        output_list.append(output_var)
    return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: output_list })


# request: form-data: { "user_id": userId } , require Token
def get_post(request):
    user_id = request.POST[FIELD_USER_ID];
    print(request.POST)

    try:
        query_values = User.objects.get(id=user_id).user_post.all().order_by('-update_time').values('id', 'title', 'content', 'abstract')
        output_list = []
        for var in query_values:
            var['abstractContent'] = var.pop('abstract')
            output_list.append(var)
        return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: output_list })
    except :
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: form-data: { "param1": post_id } 
def get_essay(request):
    post_id = request.POST[FIELD_PARAM_1];
    print(request.POST)

    query_result = Post.objects.get(id=post_id)
    if query_result:
        data = {}
        data['content'] = query_result.content;
        return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: data })
    else:
        return JsonResponse({ FIELD_REPLY: False })


# request: / , require Token
def query_draft(request): 
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    myself = request.user
    print('user: ' + str(request.user))

    try:
        query_set = Post.objects.filter(type=1, author_id=myself.id)
        query_values = query_set.order_by('-update_time').values('id', 'title', 'content', 'abstract')
        output_list = []
        for var in query_values:
            var['abstractContent'] = var.pop('abstract')
            output_list.append(var)
        return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: output_list})
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: Post , require Token
def add_post(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    request_dict = json.loads(request.body)
    print(str(request_dict) + ', user: ' + str(request.user))

    try:
        required_author = User.objects.get(id=request_dict.get('authorId'))
        if not required_author.id == request.user.id:
            return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_IDENTITY_THEFT })
        new_post = Post.objects.create(
            title = request_dict.get('title'),
            abstract = request_dict.get('abstractContent'),
            content = request_dict.get('content'),
            author = required_author,
            author_name = required_author.name,
            type = request_dict.get('type'),
            style = request_dict.get('style'),
            location = request_dict.get('location')
        )
        print('add post success, id: ' + str(new_post.id))
        return JsonResponse({ FIELD_REPLY: True })
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: Post , require Token
def update_post(request): 
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    request_dict = json.loads(request.body)
    print(str(request_dict) + ', user: ' + str(request.user))

    try:
        Post.objects.filter(id=request_dict.get('id', -1)).update(
            title = request_dict.get('title'), 
            abstract = request_dict.get('abstractContent'),
            content = request_dict.get('content'),
            type = request_dict.get('type'),
            style = request_dict.get('style'),
            location = request_dict.get('location')
        )
        print('update post success, id: ' +  str(request_dict.get('id', -1)))
        return JsonResponse({ FIELD_REPLY: True })
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: LoginData
def register(request):
    request_dict = json.loads(request.body)
    print(request_dict)

    try: 
        user_with_same_name = User.objects.get(name=request_dict.get('name'))
        if (user_with_same_name):
            print('name already registered')
            return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_REGISTER_NAME_REPEATED })
    except:
        print('valid name checked')

    try: 
        user_with_same_email = User.objects.get(email=request_dict.get('email'))
        if (user_with_same_email):
            print('email already registered')
            return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_REGISTER_EMAIL_REPEATED })
    except:
        print('valid email checked')

    try:
        new_user = User(
            name = request_dict.get('name'),
            password = request_dict.get('password'),
            email = request_dict.get('email'),
        )
        new_user.save();
        print('add user success, id: ' + str(new_user.id))
        return JsonResponse({ FIELD_REPLY: True })
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: LoginData
def login(request): 
    request_dict = json.loads(request.body)
    print(request_dict)

    user_with_token = TokenUtil.generate_token(request_dict.get('name'), request_dict.get('password'))
    if len(user_with_token) == 0:
        return JsonResponse({ FIELD_REPLY: False })    
    else:
        print(str(user_with_token))
        return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: user_with_token})


# request: User
def update_user(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    request_dict = json.loads(request.body)
    myself = request.user
    print(str(request_dict) + ', user: ' + str(request.user))

    try:
        if not myself.id == request_dict.get('id'):
            return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_IDENTITY_THEFT })
        try: 
            user_with_same_name = User.objects.get(name=request_dict.get('name'))
            if (user_with_same_name and user_with_same_name.id != myself.id):
                print('name already registered')
                return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_REGISTER_NAME_REPEATED })
        except:
            print('valid name checked')

        User.objects.filter(id=myself.id).update(
            name = request_dict.get('name'),
            introduction = request_dict.get('introduction'), 
            avatar = request_dict.get('avatarUrl'),
            password = request_dict.get('password'),
        )
        print('update post success, id: ' +  str(request_dict.get('id', -1)))
        return JsonResponse({ FIELD_REPLY: True })
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: form-data: { "user_id": user_id }
def get_user(request):
    user_id = request.POST[FIELD_USER_ID];
    print(request.POST)

    try:
        user = User.objects.get(id=user_id)
        return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: { 
            'id': user.id, 
            'name': user.name, 
            'introduction': user.introduction,
            'avatarUrl': user.avatar,
            'email': user.email,
        }})
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: form-data: { "request_data": media_data, "param1": type } , require Token
def upload_media(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    media_data = request.FILES[FIELD_PARAM_DATA]
    type = int(request.POST[FIELD_PARAM_1])
    print('type:' + str(type) + ', media-data: ' + media_data.name + ', user: ' + str(request.user))

    try:
        if type == 1: # image
            save_path = '%s/images/%s'%(settings.MEDIA_ROOT, media_data.name)
            with open(save_path, 'wb') as f :
                for content in media_data.chunks():
                    f.write(content)
            PictureRecorder.objects.create(image='images/%s'%media_data.name)

        if type == 2: # audio
            save_path = '%s/audios/%s'%(settings.MEDIA_ROOT, media_data.name)
            with open(save_path, 'wb') as f :
                for content in media_data.chunks():
                    f.write(content)
            AudioRecorder.objects.create(audio='audios/%s'%media_data.name)

        if type == 3: # video
            save_path = '%s/videos/%s'%(settings.MEDIA_ROOT, media_data.name)
            with open(save_path, 'wb') as f :
                for content in media_data.chunks():
                    f.write(content)
            VideoRecorder.objects.create(video='videos/%s'%media_data.name)

        return JsonResponse({ FIELD_REPLY: True })
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: form-data: { "user_id": user_name } , require Token
def follow(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    user_id = request.POST[FIELD_USER_ID]
    myself = request.user
    print(str(request.POST) + ', user: ' + str(request.user))

    try:
        user_followed = User.objects.get(id=user_id)
        try: 
            old_follower = Follower.objects.get(star__id=user_followed.id, fan__id=myself.id)
            if (old_follower):
                print('already followed')
                return JsonResponse({ FIELD_REPLY: True })
        except:
            pass

        if user_id == myself.id:
            return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_OPERATE_TO_YOURSELF})

        Follower.objects.create(star=user_followed, fan=myself)
        return JsonResponse({ FIELD_REPLY: True })
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: / , require Token
def get_following(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    myself = request.user
    print('user: ' + str(request.user))

    query_values = myself.user_fan.all()
    output_list = []
    for var in query_values:
        star = var.star
        output = {}
        output['id'] = star.id
        output['name'] = star.name
        output['introduction'] = star.introduction
        output['avatarUrl'] = star.avatar
        output_list.append(output)

    return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: output_list })


# request: / , require Token
def get_followed(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    myself = request.user
    print('user: ' + str(request.user))

    query_values = myself.user_star.all()
    output_list = []
    for var in query_values:
        fan = var.fan
        output = {}
        output['id'] = fan.id
        output['name'] = fan.name
        output['introduction'] = fan.introduction
        output['avatarUrl'] = fan.avatar
        output_list.append(output)

    return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: output_list })


# request: form-data: { "user_id": user_id } , require Token
def add_block(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    user_id = request.POST[FIELD_USER_ID]
    myself = request.user
    print(str(request.POST) + ', user: ' + str(request.user))
    
    try:
        user_blocked = User.objects.get(id=user_id)
        try: 
            old_blocker= Blocker.objects.get(enemy__id=user_blocked.id, host__id=myself.id)
            if (old_blocker):
                print('already blocked')
                return JsonResponse({ FIELD_REPLY: True })
        except:
            pass
        Blocker.objects.create(enemy=user_blocked, host=myself)
        return JsonResponse({ FIELD_REPLY: True })
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: / , require Token
def get_block(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    myself = request.user
    print('user: ' + str(request.user))

    query_values = myself.user_enemy.all()
    output_list = []
    for var in query_values:
        enemy = var.enemy
        output = {}
        output['id'] = enemy.id
        output['name'] = enemy.name
        output['introduction'] = enemy.introduction
        output['avatarUrl'] = enemy.avatar
        output_list.append(output)

    return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: output_list })


# request: Comment , require Token
def comment_post(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    request_dict = json.loads(request.body)
    print(str(request_dict) + ', user: ' + str(request.user))

    try:
        new_comment = Comment.objects.create(
            content = request_dict.get('content'),
            post = Post.objects.get(id=int(request_dict.get('postId'))),
            author = User.objects.get(id=int(request_dict.get('authorId'))),
        )
        print('add comment success, id: ' + str(new_comment.id))
        return JsonResponse({ FIELD_REPLY: True })
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False }) 


# request: form-data: { "post_id": post_id } 
def comment_get(request):
    post_id = request.POST[FIELD_POST_ID];
    print(request.POST)

    query_result = Post.objects.get(id=post_id).post_comment.all()
    output_list = []
    for var in query_result:
        output = {}
        output['id'] = var.id
        output['authorId'] = var.author.id
        output['content'] = var.content
        output_list.append(output)

    return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: output_list })


# request: form-data: { "post_id": post_id } , require Token
def like(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    post_id = request.POST[FIELD_POST_ID];
    myself = request.user
    print(str(request.POST) + ', user: ' + str(request.user))

    try:
        liker = Liker.objects.get(reader__id=myself.id, post__id=post_id)
    except:
        try: 
            post = Post.objects.get(id=post_id)
            post.like_num = post.like_num + 1
            post.save()
            Liker.objects.create(reader=User.objects.get(id=myself.id), post=Post.objects.get(id=post_id))
            return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: True})
        except:
            traceback.print_exc()
            return JsonResponse({ FIELD_REPLY: False }) 
    try:
        liker.state = not liker.state
        liker.save()
        post = Post.objects.get(id=post_id)
        if liker.state:
            post.like_num = post.like_num + 1
            post.save()
            return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: True})
        else :
            post.like_num = post.like_num - 1
            post.save()
            return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: False})
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False }) 


# request: Notice
def add_notice(request):
    request_dict = json.loads(request.body)
    print(str(request_dict) + ', user: ' + str(request.user))

    try:
        receiver_user = User.objects.get(id=request_dict.get('userId'))
        new_notice = Notice.objects.create(
            time = request_dict.get('time'),
            content = request_dict.get('content'),
            receiver = receiver_user
        )
        print('add notice success, id: ' + str(new_notice.id))
        return JsonResponse({ FIELD_REPLY: True })
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: form-data: { "notice_id": notice_id } , require Token
def read_notice(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    notice_id = request.POST[FIELD_NOTICE_ID];
    myself = request.user

    try:
        notice = Notice.objects.get(id=notice_id)
        if not notice.receiver.id == myself.id:
            return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_IDENTITY_THEFT })
        notice.read = True
        notice.save()
        return JsonResponse({ FIELD_REPLY: True })
    except:
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })


# request: / , require Token
def get_notice(request):
    if (not TokenUtil.check_token(request)):
        return JsonResponse({ FIELD_REPLY: False, FIELD_ERROR: ERROR_TOKEN_INVALID })
    myself = request.user
    print('user: ' + str(request.user))

    try:
        query_values = User.objects.get(id=myself.id).user_notice.all().order_by('-id').values('id', 'time', 'content', 'read')
        output_list = []
        for var in query_values:
            var['readOr'] = var.pop('read')
            output_list.append(var)
        return JsonResponse({ FIELD_REPLY: True, FIELD_DATA: output_list })
    except :
        traceback.print_exc()
        return JsonResponse({ FIELD_REPLY: False })



    